#include "ConnectPool.h"
#include <fstream>
#include <iostream>
#include <jsoncpp/json/json.h>

using namespace std;

/* 提供一个静态方法，让外部调用唯一实例 */
ConnectionPool *ConnectionPool::getConnectPool()
{
    static ConnectionPool pool;         // 静态局部变量，只能通过该函数进行访问，但是生命周期到程序结束才会析构
                                        // 连接池只会被创建一次
    return &pool;
}

/* 析构函数 */
ConnectionPool::~ConnectionPool()
{
    while(!m_connectQueue.empty())
    {
        MysqlConn * conn = m_connectQueue.front();
        m_connectQueue.pop();
        delete conn;
    }
}

/* 从连接池中取出一个Mysql连接 */
std::shared_ptr<MysqlConn> ConnectionPool::getConnection()
{
    /* 得到的MysqlDataBase对象一定是已经连接上服务器的 */
    /*
        1.判断连接池中是否有可用的连接
        2.如果没有，就条件等待
    */

    /* 加锁 */
    unique_lock<mutex> locker(m_mutex);
    while(m_connectQueue.empty())       // 判断连接池是否为空 
    {
        /* 阻塞等待条件变量，wait_for指定阻塞时间长度 */
        if(cv_status::timeout == m_cond_consume.wait_for(locker, chrono::milliseconds(m_timeout)))
        {
            /* 到这里说明，阻塞时间到了，但是队列依然为空 */
            if(m_connectQueue.empty())
            {
                /* 让线程继续阻塞 */
                continue;       // 和while相配使用
            }
        }
    }
    /* 程序到这个地方：数据库连接池有可用的连接 */
    /* 用智能指针管理，这样就不用写额外的归还连接的函数了*/
    shared_ptr<MysqlConn> connPtr(m_connectQueue.front(), [this](MysqlConn * conn)  
    // 第二个参数是该指针的删除器，而不是直接进行析构，避免了多次对数据库时连接断开 
    // 写了一个匿名函数，[]捕捉的外部变量的方式，this可以使用类的各个函数及对象，()参数，删除器参数与智能指针所管理的内存类型一定是相同的
    {
        lock_guard<mutex> locker(m_mutex);   // 自动加锁解锁，作用域为{}
        /* 刷新时间 */
        conn->refreshAliveTime();
        /* 添加到连接池里面 */
        m_connectQueue.push(conn);
        
    });

    /* 取出队头元素 */
    m_connectQueue.pop();

    /* 唤醒生产者 */
    m_cond_produce.notify_all();

    /* 得到的MysqlDataBase对象一定是已经连接上服务器，*/
    return connPtr;
}

/* 构造函数私有化 */
ConnectionPool::ConnectionPool()
{
    /* 解析json配置文件 */
    if(parseJsonFile("./config.json") == false)
    {
        return;
    }

    /* 初始化数据库，将连接添加到连接池中 */
    for(int idx = 0; idx < m_minSize; idx++)
    {
        addConnection();
    }

    /* 生产者：负责创建新连接 制定对应的任务函数，指定为类的非静态成员函数 
        传入的参数就是当前的实例对象，也就是当前的连接池 */
    thread produce(&ConnectionPool::produceConnection, this);

    /* 回收者：负责销毁空闲连接 */
    thread recycle(&ConnectionPool::recycleConnection, this);

    /* 主线程不能阻塞，所以不能用join，要主线程和子线程分离 */
    produce.detach();
    recycle.detach();
}

/* 解析配置文件 */
bool ConnectionPool::parseJsonFile(const string &path)
{
    ifstream ifs(path);
    if(ifs.is_open() == false)
    {
        cout << "failed to open config file." << endl;
        return false;
    }
    /* Jsoncpp的库 */
    Json::Reader rd;
    Json::Value root;

    if(rd.parse(ifs, root) == false)            //解析json文件传递给root
    {
        cout << "json parse failed." << endl;
        return false;
    }

    /* 判断读出的数据是否是json文件 */
    if(root.isObject())
    {
        m_hostname = root["hostname"].asString();
        m_username = root["username"].asString();
        m_passwd = root["passwd"].asString();
        m_port = root["port"].asInt();
        m_dbname = root["dbname"].asString();
        m_minSize = root["minSize"].asInt();
        m_maxSize = root["maxSize"].asInt();
        m_maxIdleTime = root["maxIdleTime"].asInt();
        m_timeout = root["timeout"].asInt();
        return true;
    }
    
    return false;
}

/* 添加连接到连接池中 */
bool ConnectionPool::addConnection()
{
    /* 创建数据库连接 */
    MysqlConn * conn = new MysqlConn();     // 新建数据库对象
    
    /* 连接数据库 */
    conn->connect(m_hostname, m_username, m_passwd, m_dbname);

    /* 记录在数据库建立连接的时间 */
    conn->refreshAliveTime();

    /* 添加连接到连接池队列*/
    m_connectQueue.push(conn);

    return true;

}

/* 生产线程入口 - 向数据库连接池生产连接句柄 */
void ConnectionPool::produceConnection()
{
    while(true)             // 不能退出
    {
        /* unique_lock智能锁，locker对象管理互斥锁，不需要手动加锁解锁 */
        unique_lock<mutex> locker(m_mutex);
        while(m_connectQueue.size() >= m_minSize)       // while多次判断连接是否够用，不用if，因为要多次判断
        {
            /* 阻塞等待条件变量 */
            m_cond_produce.wait(locker);
        }
        /* 添加连接到连接池 */
        addConnection();

        /* 唤醒消费者 */
        m_cond_consume.notify_all();
    }
}

/* 回收线程 - 回收数据库连接池的连接 */
void ConnectionPool::recycleConnection()
{
    while(true)
    {
        /* 周期性检测：C++11休眠1s */
        this_thread::sleep_for(chrono::seconds(1));

        /* 共享资源加锁 */
        unique_lock<mutex> locker(m_mutex);

        /* 销毁连接池条件 ：连接池队列连接数量大于最小的连接数 */
        while(m_connectQueue.size() > m_minSize)
        {
            /* 查看队头以判断空闲时长 */
            MysqlConn * conn = m_connectQueue.front();

            /* 存活时间 = 当前时间 - 上次操作时间 毫秒*/
            if(conn->getAliveTime() >= m_maxIdleTime)
            {
                /* 弹出对头 */
                m_connectQueue.pop();
                delete conn;
                conn = nullptr;
            }
            else
            {
                break;
            }
        }
    }
}



